package club.gskj.gulimall.ware.service.impl;

import club.gskj.common.exception.NoStockException;
import club.gskj.common.to.mq.OrderTo;
import club.gskj.common.to.mq.SotckDetailTo;
import club.gskj.common.to.mq.StockLockedTo;
import club.gskj.common.utils.R;
import club.gskj.gulimall.ware.entity.WareOrderTaskDetailEntity;
import club.gskj.gulimall.ware.entity.WareOrderTaskEntity;
import club.gskj.gulimall.ware.feign.OrderFeignService;
import club.gskj.gulimall.ware.feign.ProductFeignService;
import club.gskj.gulimall.ware.service.WareOrderTaskDetailService;
import club.gskj.gulimall.ware.service.WareOrderTaskService;
import club.gskj.gulimall.ware.vo.OrderItemVo;
import club.gskj.gulimall.ware.vo.OrderVo;
import club.gskj.gulimall.ware.vo.SkuHasStockVo;
import club.gskj.gulimall.ware.vo.WareSkuLockVo;
import com.alibaba.fastjson.TypeReference;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import club.gskj.common.utils.PageUtils;
import club.gskj.common.utils.Query;

import club.gskj.gulimall.ware.dao.WareSkuDao;
import club.gskj.gulimall.ware.entity.WareSkuEntity;
import club.gskj.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private OrderFeignService orderFeignService;


    /***
     *  解锁库存
     * @param skuId  商品id
     * @param wareId 库存id
     * @param count 锁定的库存数量
     * @param taskDetailId 库存工作单详情id
     */
    public void unLockStock(Long skuId, Long wareId, Integer count, Long taskDetailId) {
        this.baseMapper.unLockStock(skuId, wareId, count);

        // 更新 库存工作详情单状态为 已解锁
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(taskDetailId);
        // 已解锁状态
        wareOrderTaskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(wareOrderTaskDetailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /*
        skuId: 1
        wareId: 1
         */
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params), wrapper);

        return new PageUtils(page);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        // 先查询库存信息，如果没有这条库存记录，则新增
        List<WareSkuEntity> list = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (list == null || list.size() == 0) {
            // 新增库存
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(0);
            // TODO 调用远程服务，查询商品name
            try {
                R info = productFeignService.info(skuId);
                if (info.getCode() == 0) {
                    Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                    String skuName = (String) skuInfo.get("skuName");
                    wareSkuEntity.setSkuName(skuName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.save(wareSkuEntity);
        } else {
            // 修改原有库存数量
            this.baseMapper.updateStock(skuId, wareId, skuNum);
        }
    }

    @Override
    public List<SkuHasStockVo> hasStock(List<Long> skuIds) {

        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            //  查询sku的的总库存量 = (库存总数-锁定库存总数)
            //SELECT SUM(stock-stock_locked) FROM `wms_ware_sku` WHERE sku_id = 7
            skuHasStockVo.setSpuId(skuId);

            Long stockCount = this.baseMapper.selectSkuStock(skuId);
            if (stockCount != null && stockCount > 0) {
                skuHasStockVo.setHasStock(true);
            } else {
                skuHasStockVo.setHasStock(false);
            }
            return skuHasStockVo;
        }).collect(Collectors.toList());
        return collect;
    }

    /***
     *
     * 库存解锁场景。
     * 1. 下订单成功，订单过期没有支付被系统自动取消，被用户手动取消，都要解锁库存
     *
     * 2. 下单成功，库存锁定成功，接下来的业务调用抛出异常，导致订单回滚，
     *      之前锁定的库存就要自动解锁，
     */
    @Transactional(rollbackFor = NoStockException.class)
    @Override
    public Boolean orderLockStock(WareSkuLockVo wareSkuLockVo) {
        // 保存库存工作单信息
        // 方便追溯、回滚
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        // 订单编号
        wareOrderTaskEntity.setOrderSn(wareSkuLockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        List<OrderItemVo> orderItems = wareSkuLockVo.getOrderItems();
        // 找到每个商品在那个仓库都有库存
        List<SkuWareHasStock> collect = orderItems.stream().map(item -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            skuWareHasStock.setSkuId(item.getSkuId());
            skuWareHasStock.setLockCount(item.getCount());

            // 查询那些仓库中有该商品的库存信息
            List<Long> wareIds = this.baseMapper.listWareIdForHasSkuStock(item.getSkuId());
            skuWareHasStock.setWareId(wareIds);
            return skuWareHasStock;
        }).collect(Collectors.toList());

        // 遍历查询到的 商品库存信息，进行库存锁定
        for (SkuWareHasStock skuWareHasStock : collect) {
            // 当前商品库存锁定标识
            Boolean skuLocked = false;
            // 商品id
            Long skuId = skuWareHasStock.getSkuId();
            // 商品锁定数量
            Integer lockCount = skuWareHasStock.getLockCount();
            // 判断仓库是否有对应的 商品库存信息
            List<Long> wareIds = skuWareHasStock.getWareId();
            // 没有对应的商品库存信心，直接抛出异常
            if (wareIds == null || wareIds.size() == 0) {
                throw new NoStockException(skuId);
            }
            // 1. 当一个商品锁定成功后，将当前商品锁定了的几件工作单记录发给MQ
            // 2. 锁定失败：
            for (Long wareId : wareIds) {
                // 锁定成功返回1, 失败返回0
                Long updateCount = this.baseMapper.lockSkuStock(skuId, wareId, lockCount);
                if (updateCount == 1) {
                    skuLocked = true;
                    //锁定成功
                    // 保存库存工作单详情
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(skuId);
                    wareOrderTaskDetailEntity.setSkuNum(lockCount);
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    wareOrderTaskDetailEntity.setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                    SotckDetailTo sotckDetailTo = new SotckDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity, sotckDetailTo);

                    // 封装消息队列发送对象
                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setSotckDetailTo(sotckDetailTo);
                    // 工作单id
                    stockLockedTo.setSotckId(wareOrderTaskEntity.getId());
                    // 工作单详情id
                    stockLockedTo.setDetailId(wareOrderTaskDetailEntity.getId());
                    // TODO 发送消息告诉 MQ 库存锁定成功
                    rabbitTemplate.convertAndSend("stock-event-exchange",
                            "stock.locked",
                            stockLockedTo);
                    break;
                } else {
                    // 当前仓库锁库存失败，进行下一个仓库继续锁定
                }
            }
            // 只要有一个商品未锁定成功，则全部为失败
            if (!skuLocked) {
                throw new NoStockException(skuId);
            }
        }
        // 说明全部商品都已锁定成功
        return true;
    }

    @Override
    public void unLock(StockLockedTo to) {
        // 库存工作单id
        Long sotckId = to.getSotckId();

        // 库存工作单详情信息
        SotckDetailTo sotckDetailTo = to.getSotckDetailTo();
        Long id = sotckDetailTo.getId();
        // 自动解锁
        // 1.查询数据库关于这个订单的锁定库存信息
        // 有数据： 执行解锁库存操作
        //  解锁： 订单情况
        //      1.没有这个订单、必须解锁
        //       2. 有这个订单，不是解锁库存
        //          根据订单状态： 已取消、解锁库存
        //                       没取消、不能解锁
        // 没有： 库存锁定失败了、库存回滚了，这种情况无需解锁库存
        WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(id);
        if (byId != null) {
            // 解锁
            // 获取库存工作单信息
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(sotckId);
            // 获取订单编号
            String orderSn = wareOrderTaskEntity.getOrderSn();
            // 调用订单远程服务
            R r = orderFeignService.getOrderStatusByOrderSn(orderSn);
            if (r.getCode() == 0) {
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });

                // 获取订单状态
                // 订单不存在、订单已取消
                // 判断订单是否已经被取消

                if (data == null || data.getStatus() == 4) {
                    // 取消，解锁库存
                    // 判断只有已锁定状态的库存才可以解锁
                    if (byId.getLockStatus() == 1) {
                        unLockStock(sotckDetailTo.getSkuId(), sotckDetailTo.getWareId(), sotckDetailTo.getSkuNum(), sotckDetailTo.getId());
                        // 解锁库存成功，才能回复Mq
                    }
                }
            } else {
                // 拒绝此次消息回复, 将消息重新放到队列中，让别人继续消费解锁
                throw new RuntimeException("远程服务orderFeign调用失败！");
            }
        } else {
            // 无需解锁
        }
    }

    /***
     * 防止由于订单服务网络问题卡顿，导致订单状态一直无法改变，库存延迟队列优先到期，查询订单状态一直为新建，库存则不做什么操作就把消息消费了
     * 导致卡顿的订单，永远无法解锁库存
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unLock(OrderTo orderTo) {
        // 订单编号
        String id = orderTo.getOrderSn();
        // 查询当前订单对应的最新的库存工作单状态，防止重复解锁库存
        WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", id));

        // 按照库存工作单查询所有 未解锁的库存工作单详情信息
        QueryWrapper<WareOrderTaskDetailEntity> wareOrderTaskDetailEntityQueryWrapper = new QueryWrapper<>();
        wareOrderTaskDetailEntityQueryWrapper.eq("task_id", wareOrderTaskEntity.getId()).eq("lock_status", "1");
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(wareOrderTaskDetailEntityQueryWrapper);
        for (WareOrderTaskDetailEntity wareOrderTaskDetailEntity : list) {
            // 解锁库存
            unLockStock(wareOrderTaskDetailEntity.getSkuId(), wareOrderTaskDetailEntity.getWareId(), wareOrderTaskDetailEntity.getSkuNum(), wareOrderTaskDetailEntity.getId());
        }
    }

    /**
     * 封装每个商品对应在那个仓库中有数量
     */
    @Data
    class SkuWareHasStock {
        /**
         * 商品id
         */
        private Long skuId;
        /**
         * 库存仓库id
         */
        private List<Long> wareId;

        /**
         * 锁定的商品数量
         */
        private Integer lockCount;
    }
}
